package com.pactera.asmp.server.service;

import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.pactera.asmp.server.common.constants.Constants;
import com.pactera.asmp.server.common.constants.ResultCode;
import com.pactera.asmp.server.common.constants.SubTaskStatus;
import com.pactera.asmp.server.common.exception.CustomException;
import com.pactera.asmp.server.common.websocket.HeartBeatService;
import com.pactera.asmp.server.common.websocket.SocketUtil;
import com.pactera.asmp.server.common.websocket.WebSocketServerDevice;
import com.pactera.asmp.server.common.websocket.WebSocketServerScreen;
import com.pactera.asmp.server.dao.DeviceInfoMapper;
import com.pactera.asmp.server.dao.MasterComputerMapper;
import com.pactera.asmp.server.dao.SubtaskMapper;
import com.pactera.asmp.server.entity.*;
import com.pactera.asmp.server.job.dispatch.impl.feedback.FeedbackManager;
import com.pactera.asmp.server.pojo.DeviceInfo;
import com.pactera.asmp.server.pojo.DeviceInfoWithProject;
import com.pactera.asmp.server.pojo.MasterComputer;
import com.pactera.asmp.server.pojo.MasterProjectDevicePO;
import com.pactera.asmp.server.pojo.ProjectInfo4Device;
import com.pactera.asmp.server.pojo.WSMessage;
import com.pactera.asmp.server.utils.AssertUtil;
import com.pactera.asmp.server.utils.PageDataResult;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.websocket.Session;
import java.io.IOException;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;

/**
 * @ProjectName: tlsserver
 * @Package com.pactera.asmp.server.service
 * @ClassName: DeviceInfoImpl
 * @Description: DeviceInfoImpl
 * @Date 2020/06/28 17:03
 * @Author lvzhiqiang
 * @Version 1.0
 */
@Service
public class DeviceServiceImpl implements IDeviceServcie {

    private final static String DEVICE_NOTNULL = "设备Id不允许为空";
    private final static String USERID_NOTNULL = "用户Id不允许为空";
    private final static String TASKID_NOTNULL = "任务Id不允许为空";

    private static Logger logger = LoggerFactory.getLogger(DeviceServiceImpl.class.getSimpleName());
    private static final ObjectMapper objectMapper = new ObjectMapper();

    @Autowired
    private DeviceInfoMapper mDeviceInfoMapper;
    @Autowired
    private RedisServiceImpl mRedisServiceImpl;

    @Autowired
    @Lazy
    private SocketUtil socketUtil;

    @Autowired
    @Lazy
    private TaskService taskService;

    @Autowired
    private MasterComputerMapper masterComputerMapper;

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    @Lazy
    private HeartBeatService heartBeatService;

    @Autowired
    private FeedbackManager feedbackManager;

    @Autowired
    private SubtaskMapper subtaskMapper;

    @Override
    @Transactional
    public boolean insert(DeviceInfo deviceInfo) {
        if(CollectionUtils.isNotEmpty(deviceInfo.getDisplayIDs())) {
            deviceInfo.setDisplayIdsStr(JSONObject.toJSONString(deviceInfo.getDisplayIDs()));
        }
        DeviceInfo existDevice = mDeviceInfoMapper.selectByDuid(deviceInfo.getDuid());
        int count =0;
        if (existDevice != null) {
            deviceInfo.setId(existDevice.getId());
            if(deviceInfo.getStatus() == 2) {
                deviceInfo.setDisconnectTime(LocalDateTime.now());
            }
            count =  mDeviceInfoMapper.update(deviceInfo) ;
        }
        else{
            deviceInfo.setRegisterTime(LocalDateTime.now());
            count = mDeviceInfoMapper.insert(deviceInfo) ;
        }
        return count >0;
    }

    @Override
    public boolean delete(Integer id) {
        return mDeviceInfoMapper.deleteByPrimaryKey(id) > 0;
    }

    @Override
    public boolean delete(List<Integer> datas) {
        return mDeviceInfoMapper.deleteByPrimaryKeys(datas) > 0;
    }

    @Override
    public boolean update(DeviceInfo deviceInfo) {
        if(deviceInfo.getStatus() == 2) {
            deviceInfo.setDisconnectTime(LocalDateTime.now());
        }
        if(CollectionUtils.isNotEmpty(deviceInfo.getDisplayIDs())) {
            deviceInfo.setDisplayIdsStr(JSONObject.toJSONString(deviceInfo.getDisplayIDs()));
        }
        return mDeviceInfoMapper.update(deviceInfo) > 0;
    }

    @Override
    @Transactional
    public boolean operationDevice(String duid, Integer userId, Integer status, String userName) {
        AssertUtil.notNull(duid, DEVICE_NOTNULL);
        AssertUtil.notNull(userId,USERID_NOTNULL);
        AssertUtil.notNull(status,"状态不允许为空");
        AssertUtil.notNull(userName,"用户姓名不允许为空");

        DeviceInfo exist = mDeviceInfoMapper.selectByDuid(duid);
        if(exist == null || exist.getStatus() == 2){
            throw new CustomException(ResultCode.DEVICE_NOTEXIST_OR_OFFLINE);
        }
        if(exist.getStatus() == 1 && !Objects.equals(exist.getUserId(), userId)) {
            throw new CustomException(ResultCode.DEVICE_NOT_AVAILABLE);
        }

        // 当前用户如果存在已经占用的设备时，不允许再绑定其他设置
        if(status == 1){
            List<DeviceInfoSearchDTO> deviceBindByUserIds = mDeviceInfoMapper.findDeviceBindByUserId(userId);
            if(CollectionUtils.isNotEmpty(deviceBindByUserIds)) {
                Optional<DeviceInfoSearchDTO> optional = deviceBindByUserIds.stream().filter(dto -> dto.getIsAuxiliary() == 0).findAny();
                if(null != optional && optional.isPresent()) {
                    if (optional.get().getId() != null && !duid.equalsIgnoreCase(optional.get().getDuid()))
                        throw new CustomException(ResultCode.BUSINESS_DEVICE_BIND_MORE_ERR);
                    if (optional.get().getId() != null && duid.equalsIgnoreCase(optional.get().getDuid()))
                        return true;
                }
            }
        }

        DeviceInfo deviceInfo = new DeviceInfo();
        deviceInfo.setDuid(duid);
        deviceInfo.setStatus(status);
        deviceInfo.setUserId(userId);
        deviceInfo.setUserName(userName);
        if(status == 2) {
            deviceInfo.setDisconnectTime(LocalDateTime.now());
        }
        boolean result = mDeviceInfoMapper.updateStatus(deviceInfo) > 0;
        if (result){
            switch (status) {
                case 1:
                    String s = mRedisServiceImpl.get(String.valueOf(userId));
                    if(!StringUtils.isEmpty(s)) mRedisServiceImpl.remove(s);  // 一个设备只能绑一个用户，所以用户在切换设备时，需要将原先绑定的设备移除
                    mRedisServiceImpl.set(duid, String.valueOf(userId));
                    mRedisServiceImpl.set(String.valueOf(userId), duid);
                    break;
                default:
                    mRedisServiceImpl.remove(duid);
                    mRedisServiceImpl.remove(String.valueOf(userId));

                    Long size = mRedisServiceImpl.getRedisTemplate().opsForSet().size(SocketUtil.REDIS_KEY_SET_SCREEN_DEVICE_SESSION + ":" + duid);
                    if(size == null || size == 0){ // 没有大屏用户绑定时，需要通知设备不再发送图片
                        // 发送MQ消息
                        WSMessage message = new WSMessage();
                        logger.error("Device 连接已断开");
                        message.setMsgcode(WebSocketServerScreen.SCREEN_CODE_DISCONNECTED_IMAGE);
                        message.setDescribe("连接已断开");
                        try {
                            socketUtil.sendMQ(String.valueOf(userId),duid, objectMapper.writeValueAsBytes(message), "json",null);
                        } catch (JsonProcessingException e) {
                            logger.info(e.getMessage());
                        }
                    }
                    break;
            }
        }

        return result;
    }


    @Override
    @Transactional
    public boolean operationDeviceForAux(String duid, Integer userId, Integer status, String userName) {
        AssertUtil.notNull(duid, DEVICE_NOTNULL);
        AssertUtil.notNull(userId,USERID_NOTNULL);
        AssertUtil.notNull(status,"状态不允许为空");
        AssertUtil.notNull(userName,"用户姓名不允许为空");

        DeviceInfo exist = mDeviceInfoMapper.selectByDuid(duid);
        if(exist == null || exist.getStatus() == 2){
            throw new CustomException(ResultCode.DEVICE_NOTEXIST_OR_OFFLINE);
        }
        if(exist.getStatus() == 1 && !Objects.equals(exist.getUserId(), userId)) {
            throw new CustomException(ResultCode.DEVICE_NOT_AVAILABLE);
        }

        // 当前用户如果存在已经占用辅助设备时，不允许再绑定其他辅助设备
        if(status == 1){
            List<DeviceInfoSearchDTO> deviceBindByUserIds = mDeviceInfoMapper.findDeviceBindByUserId(userId);
            if(CollectionUtils.isNotEmpty(deviceBindByUserIds)) {
                Optional<DeviceInfoSearchDTO> optional = deviceBindByUserIds.stream().filter(dto -> dto.getIsAuxiliary() == 1).findAny();
                if(null != optional && optional.isPresent()) {
                    if (optional.get().getId() != null && !duid.equalsIgnoreCase(optional.get().getDuid()))
                        throw new CustomException(ResultCode.BUSINESS_DEVICE_BIND_MORE_ERR);
                    if (optional.get().getId() != null && duid.equalsIgnoreCase(optional.get().getDuid()))
                        return true;
                }
            }
        }

        DeviceInfo deviceInfo = new DeviceInfo();
        deviceInfo.setDuid(duid);
        deviceInfo.setStatus(status);
        deviceInfo.setUserId(userId);
        deviceInfo.setUserName(userName);
        if(status == 2) {
            deviceInfo.setDisconnectTime(LocalDateTime.now());
        }
        boolean result = mDeviceInfoMapper.updateStatus(deviceInfo) > 0;
        if (result){
            switch (status) {
                case 1:
                    String s = mRedisServiceImpl.get(String.valueOf(userId)+"aux");
                    if(!StringUtils.isEmpty(s)) mRedisServiceImpl.remove(s);  // 一个设备只能绑一个用户，所以用户在切换设备时，需要将原先绑定的设备移除
                    mRedisServiceImpl.set(duid, "aux" + String.valueOf(userId));
                    mRedisServiceImpl.set(String.valueOf(userId)+"aux", duid);
                    break;
                default:
                    mRedisServiceImpl.remove(duid);
                    mRedisServiceImpl.remove(String.valueOf(userId));

                    Long size = mRedisServiceImpl.getRedisTemplate().opsForSet().size(SocketUtil.REDIS_KEY_SET_SCREEN_DEVICE_SESSION + ":" + duid);
                    if(size == null || size == 0){ // 没有大屏用户绑定时，需要通知设备不再发送图片
                        // 发送MQ消息
                        WSMessage message = new WSMessage();
                        logger.error("Device 连接已断开");
                        message.setMsgcode(WebSocketServerScreen.SCREEN_CODE_DISCONNECTED_IMAGE);
                        message.setDescribe("连接已断开");
                        try {
                            socketUtil.sendMQ(String.valueOf(userId),duid, objectMapper.writeValueAsBytes(message), "json",null);
                        } catch (JsonProcessingException e) {
                            logger.info(e.getMessage());
                        }
                    }
                    break;
            }
        }

        return result;
    }

    @Override
    public DeviceInfo get(Integer id) {
        return mDeviceInfoMapper.selectByPrimaryKey(id);
    }

    @Override
    public PageDataResult get(DeviceInfoSearchDTO deviceInfoSearchDTO, int page, int limit) {
        PageDataResult pageDataResult = new PageDataResult();
        PageHelper.startPage(page, limit);
        List<DeviceInfo> list = mDeviceInfoMapper.getDevices(deviceInfoSearchDTO);
        PageInfo<DeviceInfo> pageInfo = new PageInfo<>(list);
        pageDataResult.setTotals((int) pageInfo.getTotal());
        pageDataResult.setList(list);
        return pageDataResult;
    }

    @Override
    public boolean updateStatus(String duid, Integer status) {
        return mDeviceInfoMapper.updateStatusByDuid(duid, status, LocalDateTime.now()) > 0;
    }

    @Override
    public DeviceInfo selectByDuidAndSn(String duid, String sn) {
        return mDeviceInfoMapper.selectByDuidAndSn(duid, sn);
    }

    @Override
    public void sendToDeviceMessage(String duid, String content) throws IOException {
        socketUtil.sendMQ("server",duid,content.getBytes("utf-8"),"json",null);
    }

    @Override
    public boolean updateAutoTestStatus1( String duid, Integer userId, String userName) {
        return mDeviceInfoMapper.updateAutoTestStatus1(duid, userId,userName) > 0;
    }

    @Override
    public boolean disconnectAutoTestDevice( String duid) {
        return mDeviceInfoMapper.disconnectAutoTestDevice(duid) > 0;
    }

    @Override
    public boolean deviceOffline(String duid) {
        return mDeviceInfoMapper.deviceOffline(duid) > 0;
    }

    @Override
    public ResponseResult handleDeviceOffline(DeviceInfo deviceInfo){
        AssertUtil.notNull(deviceInfo,"参数异常");
        AssertUtil.hasText(deviceInfo.getDuid(),DEVICE_NOTNULL);
        //
        //1. close websocket
        //2. update deviestate
        Session session = WebSocketServerDevice.id2sessionMap.get(deviceInfo.getDuid());
        if(session!=null && session.isOpen()){
            try {
                session.close();
            } catch (IOException e) {
                logger.error("close websocket", e);
            }
        }
        deviceOffline(deviceInfo.getDuid());

        return ResponseResult.defaultSuccess(null);
    }

    @Override
    public int updateDeviceTask(DeviceInfo deviceInfo) {
        AssertUtil.notNull(deviceInfo,"参数异常");
        AssertUtil.hasText(deviceInfo.getDuid(),DEVICE_NOTNULL);
        AssertUtil.notNull(deviceInfo.getTaskId(),TASKID_NOTNULL);
        AssertUtil.notNull(deviceInfo.getTaskUserId(),USERID_NOTNULL);
        AssertUtil.hasText(deviceInfo.getTaskUserName(),"用户名称不允许为空");
        return this.mDeviceInfoMapper.updateDeviceTask(deviceInfo);
    }

    @Override
    public int clearDeviceTaskInfo(String duid, Integer historyTaskId, Integer taskUserId) {
        AssertUtil.hasText(duid,DEVICE_NOTNULL);
        AssertUtil.notNull(historyTaskId,TASKID_NOTNULL);
        AssertUtil.notNull(taskUserId,USERID_NOTNULL);
        return this.mDeviceInfoMapper.clearDeviceTaskInfo(duid,historyTaskId,taskUserId);
    }

    @Override
    public int clearDeviceTaskInfo2(String duid, Integer taskId, Integer taskUserId) {
        AssertUtil.hasText(duid,DEVICE_NOTNULL);
        AssertUtil.notNull(taskId,TASKID_NOTNULL);
        AssertUtil.notNull(taskUserId,USERID_NOTNULL);
        return this.mDeviceInfoMapper.clearDeviceTaskInfo2(duid,taskId,taskUserId);
    }

    @Override
    public int deviceOfflineByDuids(Set<String> duidIds) {
        return this.mDeviceInfoMapper.deviceOfflineByDuids(duidIds);
    }

    @Override
    @Transactional
    public ResponseResult registerDevice(DeviceInfo deviceInfo) {
        if (null == deviceInfo) {
            return new ResponseResult(ResultCode.PARAM_IS_BLANK);
        }
        if (com.pactera.asmp.server.common.logprint.utils.StringUtils.isEmpty(deviceInfo.getDuid()) || com.pactera.asmp.server.common.logprint.utils.StringUtils.isEmpty(deviceInfo.getSn()) || deviceInfo.getWidth() == 0 || deviceInfo.getHeight() == 0) {
            return new ResponseResult(ResultCode.PARAM_NOT_COMPLETE);
        }
        // 如果是车机，校验上位机是否存在已绑定车机
        if (!deviceInfo.getAuxiliary() && null != deviceInfo.getMasterComputerId()) {
             List<DeviceInfo> deviceInfos = this.mDeviceInfoMapper.getDevices(new DeviceInfoSearchDTO() {{ setMasterComputerId(deviceInfo.getMasterComputerId()); setIsAuxiliary(0); }});
             if(CollectionUtils.isNotEmpty(deviceInfos) &&deviceInfos.stream()
                     .anyMatch(info -> deviceInfo.getType() == info.getType() && !StringUtils.equals(deviceInfo.getDuid(), info.getDuid()))) {
                 return new ResponseResult(ResultCode.BUSINESS_DEVICE_EXISTED);
             }
        }
        DeviceInfo oldDeviceInfo = this.mDeviceInfoMapper.selectByDuid(deviceInfo.getDuid());
        if (oldDeviceInfo != null) {
            deviceInfo.setId(oldDeviceInfo.getId());
            deviceInfo.setIsAuxiliary(deviceInfo.getAuxiliary()?1:0);
            boolean result = this.update(deviceInfo);
            logger.info("registerDevice update,result={}",result);
            if(result) {
                if (deviceInfo.getTaskId() != null) {
                    // 当设备正在执行任务，且断线重连时，告诉后端服务，当前设备正在执行的任务Id
                    Subtask subtask = subtaskMapper.selectByPrimaryKey(Long.valueOf(deviceInfo.getTaskId()));
                    //subtask更新到去取消状态，等待online
                    subtask.setTaskStatus(SubTaskStatus.FAIL_WAIT_CANCEL.getValue());
                    subtaskMapper.updateByPrimaryKeySelective(subtask);
                }
            }
            return result ? new ResponseResult(ResultCode.SUCCESS) : new ResponseResult(ResultCode.BUSINESS_DEVICE_BIND_ERR);
        }
        deviceInfo.setIsAuxiliary(deviceInfo.getAuxiliary()?1:0);
        boolean result = this.insert(deviceInfo);
        return result ? new ResponseResult(ResultCode.SUCCESS) : new ResponseResult(ResultCode.BUSINESS_DEVICE_BIND_ERR);
    }

    @Override
    public List<ProjectMasterComDeviceVo> deviceListByProjectId(Integer projectId, Integer masterComputerId,String cuid) {
        List<ProjectMasterComDeviceVo> projectMasterComDeviceVos = new ArrayList<>();
        List<DeviceDbcVo> deviceDbcVos ;
        if(cuid != null){
            deviceDbcVos = mDeviceInfoMapper.findListByCuid(projectId, cuid);
        }
        else{
            deviceDbcVos = mDeviceInfoMapper.findList(projectId, masterComputerId);
        }
        if (null == deviceDbcVos || deviceDbcVos.isEmpty()) {
            return projectMasterComDeviceVos;
        }

        Collections.sort(deviceDbcVos);

        // 根据上位机将设备分组
        Map<Integer, List<DeviceDbcVo>> deviceGroupMap = new HashMap<>();
        for (DeviceDbcVo deviceDbcVo : deviceDbcVos) {
            Integer masterComputerIdTemp = deviceDbcVo.getMasterComputerId();
            List<DeviceDbcVo> deviceDbcGroups = deviceGroupMap.get(masterComputerIdTemp);
            if (null == deviceDbcGroups) {
                deviceDbcGroups = new ArrayList<>();
                deviceGroupMap.put(masterComputerIdTemp, deviceDbcGroups);
            }
//            List<DeviceDbcVo> deviceDbcGroups = deviceGroupMap.computeIfAbsent(masterComputerIdTemp, k -> new ArrayList<>());

            String canCode = deviceDbcVo.getCanCode();
            if (org.apache.commons.lang3.StringUtils.isNotBlank(canCode)) {
                deviceDbcVo.setCanState(Constants.CAN_INFO_STATE_2);
                String redisKey = new StringBuilder(Constants.REDIS_KEY_PREFIX_CAN_BIND).append("_")
                        .append(deviceDbcVo.getCuid()).append("_")
                        .append(deviceDbcVo.getCanCode()).append("_")
                        .append(deviceDbcVo.getDuid()).toString();
                if (redisTemplate.hasKey(redisKey).booleanValue()) {
                    deviceDbcVo.setCanState(Constants.CAN_INFO_STATE_1);
                }
            }
            deviceDbcGroups.add(deviceDbcVo);
        }
        // 查询当前项目设备下的上位机
        List<MasterComputer> masterComputers = masterComputerMapper.findMasterComputerByIds(new ArrayList<>(deviceGroupMap.keySet()));
        if (null == masterComputers || masterComputers.isEmpty()) {
            return projectMasterComDeviceVos;
        }

        // 将上位机信息和设备分组列表信息关联
        for (MasterComputer masterComputer : masterComputers) {
            ProjectMasterComDeviceVo projectMasterComDeviceVo = new ProjectMasterComDeviceVo();
            projectMasterComDeviceVo.setMasterComputer(masterComputer);
            projectMasterComDeviceVo.setDeviceInfos(deviceGroupMap.get(masterComputer.getId()));
            projectMasterComDeviceVos.add(projectMasterComDeviceVo);
        }
        return projectMasterComDeviceVos;
    }

    @Override
    @Transactional
    public boolean operationDeviceForSimulator(String duid, Integer userId, Integer status, String userName) {
        AssertUtil.notNull(duid, DEVICE_NOTNULL);
        AssertUtil.notNull(userId,USERID_NOTNULL);
        AssertUtil.notNull(status,"状态不允许为空");
        AssertUtil.notNull(userName,"用户姓名不允许为空");

        DeviceInfo exist = mDeviceInfoMapper.selectByDuid(duid);
        if(exist == null || exist.getStatus() == 2){
            return false;
        }

        // 当前用户如果存在已经占用的设备时，不允许再绑定其他设置
        if(status == 1){
            DeviceInfoSearchDTO deviceBindByUserId = mDeviceInfoMapper.findDeviceBindByUserNameBySimulator(userName);
            if(deviceBindByUserId != null && deviceBindByUserId.getId() != null && !duid.equalsIgnoreCase(deviceBindByUserId.getDuid())) throw new CustomException(ResultCode.BUSINESS_DEVICE_BIND_MORE_ERR);
            if(deviceBindByUserId != null && deviceBindByUserId.getId() != null && duid.equalsIgnoreCase(deviceBindByUserId.getDuid()))  return true;
        }

        DeviceInfo deviceInfo = new DeviceInfo();
        deviceInfo.setDuid(duid);
        deviceInfo.setStatus(status);
        deviceInfo.setUserId(userId);
        deviceInfo.setUserName(userName);
        if(status == 2) {
            deviceInfo.setDisconnectTime(LocalDateTime.now());
        }
        boolean result = mDeviceInfoMapper.updateStatusForSimulator(deviceInfo) > 0;
        if (result){
            switch (status) {
                case 1:
                    mRedisServiceImpl.set(duid, userName);
                    mRedisServiceImpl.set(userName, duid);
                    break;
                default:
                    mRedisServiceImpl.remove(duid);
                    mRedisServiceImpl.remove(userName);
                    break;
            }
        }
        if(status == 1) {
            this.onlineDuidSimulator(duid, userName);
        }
        else {
            this.deviceOfflineBySimulatorId(duid,userName,status);
        }
        return result;
    }

    @Override
    public void onlineDuidSimulator(String duid,String simulatorId){
        heartBeatService.onlineDuidSimulator(duid,simulatorId);
    }

    @Override
    public void deviceOfflineBySimulatorId(String duid, String simulatorId, int duidStatus) {
        AssertUtil.hasText(duid,DEVICE_NOTNULL);
        AssertUtil.hasText(simulatorId,"simulatorId不允许为空");

        this.mDeviceInfoMapper.deviceOfflineBySimulatorId(duid,simulatorId,duidStatus, LocalDateTime.now());

        String memberId = duid +"_"+simulatorId;
        this.mRedisServiceImpl.getRedisTemplate().opsForZSet().remove(HeartBeatService.REDIS_KEY_WS_ONLINE_DUID_Simulator,memberId);
    }

    @Override
    public List<ProjectMasterDeviceVo> deviceBindByProjectId(Integer projectId) {
        AssertUtil.notNull(projectId,"项目ID不允许为空");
        List<ProjectMasterDeviceVo> list = mDeviceInfoMapper.deviceBindByProjectId(projectId);
        // 统计后排序
        int count;
        if(!CollectionUtils.isEmpty(list)){
               for(ProjectMasterDeviceVo vo : list){
                   count = 0;
                   if(!CollectionUtils.isEmpty(vo.getDeviceInfos())){
                       for(DeviceInfoWithProject device : vo.getDeviceInfos()){
                           if(device.getProjectId() != null) count++;
                       }
                       Collections.sort(vo.getDeviceInfos());
                   }
                   vo.getMasterComputer().setProDeviceCount(count);
               }
               Collections.sort(list);
        }
        return list;
    }

    @Override
    public List<MasterProjectDeviceVo> deviceBindByMasterId(Integer masterId) {
        AssertUtil.notNull(masterId,"上位机ID不允许为空");

        List<MasterProjectDevicePO> deviceBind;
        List<MasterProjectDevicePO> deviceUnBind;
        List<MasterProjectDeviceVo> result = new ArrayList<>();

        deviceUnBind = mDeviceInfoMapper.deviceUnBind();
        if(masterId == -1){
            deviceBind = new ArrayList<>();
        }
        else {
            deviceBind= mDeviceInfoMapper.deviceBindByMasterId(masterId);
        }

        this.handlerList(result,deviceBind);
        Collections.sort(result);

        if(!CollectionUtils.isEmpty(deviceUnBind)) {
            MasterProjectDeviceVo unbindVo = new MasterProjectDeviceVo();
            ProjectInfo4Device unbindProjectInfo = new ProjectInfo4Device();
            List<DeviceInfoWithProject> unbindDeviceInfos = new ArrayList<>();

            unbindVo.setProjectInfo(unbindProjectInfo);
            unbindVo.setDeviceInfos(unbindDeviceInfos);
            result.add(unbindVo);
            unbindProjectInfo.setId(-1);
            unbindProjectInfo.setProjectName("[未分配]");
            for(MasterProjectDevicePO po : deviceUnBind){
                copyDeviceValue(unbindDeviceInfos,po,new DeviceInfoWithProject());
            }
            Collections.sort(unbindDeviceInfos);
        }
        return result;
    }

    @Override
    public int updateNickName(DeviceInfo deviceInfo) {
        AssertUtil.notNull(deviceInfo,"参数异常");
        AssertUtil.notNull(deviceInfo.getId(),"设备ID不允许为空");
        AssertUtil.hasText(deviceInfo.getNickName(),"昵称不允许为空");
        AssertUtil.isTrue(deviceInfo.getNickName().matches(Constants.REGEXP_FIRST_LAST_NOT_SPACE),"昵称格式不正确");

        return mDeviceInfoMapper.updateNickName(deviceInfo);
    }

    @Override
    public List<ProjectMasterComDeviceVo> getDevicesByCuid(String cuid) {
        AssertUtil.hasText(cuid,"上位机ID不允许为空");
        return this.deviceListByProjectId(0,null,cuid);
    }

    @Override
    public List<DeviceInfo> deviceUnBindByProjectId(Integer projectId) {
        AssertUtil.notNull(projectId,"项目ID不允许为空");
        List<DeviceInfo> list = mDeviceInfoMapper.deviceUnBindByProjectId(projectId);

        if(!CollectionUtils.isEmpty(list)) {

            Collections.sort(list, new Comparator<DeviceInfo>() {
                @Override
                public int compare(DeviceInfo o1, DeviceInfo o2) {
                    int s1 = o1.getStatus();
                    int s2 = o2.getStatus();

                    if(s1 == 1) {
                        s1 = -1;
                    }
                    if(s2 == 1) {
                        s2 = -1;
                    }

                    return s1 - s2;
                }
            });
        }
        return list;
    }

    @Override
    public ResponseResult devicePackageName(DeviceInfo deviceInfo) {
        if(StringUtils.isEmpty(deviceInfo.getDuid())) {
            return ResponseResult.defaultFail(ResultCode.PARAM_IS_BLANK);
        }
        if(StringUtils.isNotEmpty(redisTemplate.opsForValue().get(SocketUtil.REDIS_KEY_WS_DEVICE_PACKAGE_NAME + ":" + deviceInfo.getDuid()))) {
            return ResponseResult.defaultSuccess(redisTemplate.opsForValue().get(SocketUtil.REDIS_KEY_WS_DEVICE_PACKAGE_NAME + ":" + deviceInfo.getDuid()));
        }
        try {
            WSMessage message = new WSMessage();
            message.setMsgcode("231");
            message.setDescribe("获取包名");
            this.sendToDeviceMessage(deviceInfo.getDuid(), JSONObject.toJSONString(message));
        } catch (IOException e) {
            logger.error("send device msg error. {}", e.getMessage());
        }
        return ResponseResult.defaultSuccess(null);
    }

    private void handlerList(List<MasterProjectDeviceVo> result, List<MasterProjectDevicePO> deviceBind) {
        MasterProjectDeviceVo vo;
        ProjectInfo4Device projectInfo = null;
        List<DeviceInfoWithProject> deviceInfos = null;

        if(!CollectionUtils.isEmpty(deviceBind)){
            for(MasterProjectDevicePO po : deviceBind){
                // Project
                if(projectInfo == null){
                    vo = new MasterProjectDeviceVo();
                    projectInfo = new ProjectInfo4Device();
                    deviceInfos = new ArrayList<>();
                    vo.setProjectInfo(projectInfo);
                    vo.setDeviceInfos(deviceInfos);
                    result.add(vo);
                }
                if(!po.getProjectId().equals(projectInfo.getId())){
                    copyProjectValue(po,projectInfo);
                }

                // Device
                copyDeviceValue(deviceInfos,po,new DeviceInfoWithProject());
            }
            Collections.sort(deviceInfos);
        }
    }

    private void copyDeviceValue(List<DeviceInfoWithProject> deviceInfos, MasterProjectDevicePO from, DeviceInfoWithProject to) {
        deviceInfos.add(to);
        to.setId(from.getDeviceId());
        to.setDuid(from.getDuid());
        to.setSn(from.getSn());
        to.setName(from.getName());
        to.setModel(from.getModel());
        to.setPlatform(from.getPlatform());
        to.setBrand(from.getBrand());
        to.setCpuAbi(from.getCpuAbi());
        to.setSdkVersion(from.getSdkVersion());
        to.setStatus(from.getStatus());
        to.setHeight(from.getHeight());
        to.setWidth(from.getWidth());
        to.setRegisterTime(from.getRegisterTime());
        to.setDisconnectTime(from.getDisconnectTime());
        to.setIconIndex(from.getIconIndex());
        to.setMasterComputerId(from.getMasterComputerId());
        to.setUserId(from.getUserId());
        to.setMasterUid(from.getMasterUid());
        to.setUserName(from.getUserName());
        to.setTaskId(from.getTaskId());
        to.setTaskUserId(from.getTaskUserId());
        to.setTaskUserName(from.getTaskUserName());
        to.setNickName(from.getNickName());
        to.setCuid(from.getCuid());
//        to.setCanCode(from.getCanCode());
//        to.setCanState(from.getCanState());
        this.canStateHandle(from,to);
    }

    private void canStateHandle(MasterProjectDevicePO from, DeviceInfoWithProject to) {
        if(from == null || to == null || StringUtils.isEmpty(from.getCuid()) || StringUtils.isEmpty(from.getDuid())) return;

        String canCode = mDeviceInfoMapper.getCanByCuidAndDuid(from.getCuid(), from.getDuid());
        to.setCanCode(canCode);
        if (!StringUtils.isEmpty(canCode)) {
            to.setCanState(Constants.CAN_INFO_STATE_2);
            String redisKey = new StringBuilder(Constants.REDIS_KEY_PREFIX_CAN_BIND).append("_")
                    .append(from.getCuid()).append("_")
                    .append(from.getCanCode()).append("_")
                    .append(from.getDuid()).toString();
            if (redisTemplate.hasKey(redisKey).booleanValue()) {
                to.setCanState(Constants.CAN_INFO_STATE_1);
            }
        }
    }

    private void copyProjectValue(MasterProjectDevicePO from, ProjectInfo4Device to) {
        if(from == null || to == null) return;

        to.setId(from.getProjectId());
        to.setProjectName(from.getProjectName());
        to.setProjectDescription(from.getProjectDescription());
        to.setProjectStatus(from.getProjectStatus());
        to.setCreateTime(from.getCreateTime());
        to.setCompletionTime(from.getCompletionTime());
        to.setUpdateTime(from.getUpdateTime());
        to.setCreateUserId(from.getCreateUserId());
        to.setUpdateUserId(from.getUpdateUserId());
        to.setDeviceBindCount(0);

    }

}
